import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author: linzp
 * @Date: 2021/10/26/16:05
 * java动态代理 aop
 * 我们上面静态代理的例子中，代理类(studentProxy)是自己定义好的，在程序运行之前就已经编译完成。
 * 然而动态代理，代理类并不是在Java代码中定义的，而是在运行时根据我们在Java代码中的“指示”动态生成的。
 * 相比于静态代理， 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理，而不用修改每个代理类中的方法。 比如说，想要在每个代理的方法前都加上一个处理方法：
 * ————————————————
 * 版权声明：本文为CSDN博主「张彦峰ZYF」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
 * 原文链接：https://blog.csdn.net/xiaofeng10330111/article/details/105633821
 */
public class Demo2 {

    static interface Person{
        void giveMoney();//学生打钱交班费
    }
    static class Student implements Person{
        private String name;
        public Student(String name) {
            this.name = name;
        }

        @Override
        public void giveMoney() {
            try {
                //假设数钱花了一秒时间
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "上交班费50元");
        }
    }

    static class MonitorUtil{//统计方法运行时间工具类
        private static ThreadLocal<Long> tl = new ThreadLocal<>();
        public static void start() {
            tl.set(System.currentTimeMillis());
        }

        //结束时打印耗时
        public static void finish(String methodName) {
            long finishTime = System.currentTimeMillis();
            System.out.println(methodName + "方法耗时" + (finishTime - tl.get()) + "ms");
        }

    }
/*
* 实现InvocationHandler接口，这个类中持有一个被代理对象的实例target。InvocationHandler中有一个invoke方法，
* 所有执行代理对象的方法都会被替换成执行invoke方法。在invoke方法中执行被代理对象target的相应方法。
* 在代理过程中，我们在真正执行被代理对象的方法前加入自己其他处理。这也是Spring中的AOP实现的主要原理
* */
    static class StuInvocationHandler<T> implements InvocationHandler {
        T target;//代理的对象

        public StuInvocationHandler(T target) {
            this.target = target;
        }
        /**
         * proxy:代表动态代理对象
         * method：代表正在执行的方法
         * args：代表调用目标方法时传入的实参
         */
         @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println( "代理执行" +method.getName() + "方法");
            //代理过程中插入监测方法,计算该方法耗时
            MonitorUtil.start();
            Object result = method.invoke(target, args);
            MonitorUtil.finish(method.getName());
            return result;
        }
    }

    public static void main(String[] args) {
        //创建一个实例对象，这个对象是被代理的对象
        Person zhangsan = new Student("zhangsan");
        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);//        InvocationHandler stuHandler = new StuInvocationHandler<>(zhangsan);

        //创建一个代理对象stuProxy来代理zhangsan，代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
        //代理执行上交班费的方法
        stuProxy.giveMoney();
        //创建了一个需要被代理的学生张三，将zhangsan对象传给了stuHandler中，我们在创建代理对象stuProxy时，将stuHandler作为参数了的，上面也有说到所有执行代理对象的方法都会被替换成执行invoke方法，也就是说，最后执行的是StuInvocationHandler中的invoke方法。
        //————————————————
        //版权声明：本文为CSDN博主「张彦峰ZYF」的原创文章，遵循CC 4.0 BY-SA版权协议，转载请附上原文出处链接及本声明。
        //原文链接：https://blog.csdn.net/xiaofeng10330111/article/details/105633821

    }

}
